package com.lkdont.android.media.opengl

import android.opengl.GLES11Ext
import android.opengl.GLES20
import android.opengl.GLException
import android.opengl.Matrix
import java.nio.FloatBuffer

/**
 * 视频帧绘制程序
 *
 * @author lqd
 */
class FrameProgram : GLProgram(FrameVertexShader(), FrameFragmentShader()) {

    private val muMVPMatrixLoc: Int
    private val muTexMatrixLoc: Int
    private val maPositionLoc: Int
    private val maTextureCoordLoc: Int

    /*** 关联的SurfaceTexture的id */
    var textureId: Int = 0

    init {
        // 初始化
        val program = handle
        muMVPMatrixLoc = GLES20.glGetUniformLocation(program, "uMVPMatrix")
        checkLocation(muMVPMatrixLoc, "uMVPMatrix")
        muTexMatrixLoc = GLES20.glGetUniformLocation(program, "uTexMatrix")
        checkLocation(muTexMatrixLoc, "uTexMatrix")
        maPositionLoc = GLES20.glGetAttribLocation(program, "aPosition")
        checkLocation(maPositionLoc, "aPosition")
        maTextureCoordLoc = GLES20.glGetAttribLocation(program, "aTextureCoord")
        checkLocation(maTextureCoordLoc, "aTextureCoord")
    }

    /**
     * Creates a texture object suitable for use with this program.
     * On exit, the texture will be bound.
     */
    fun createTextureObject(): Int {
        val textures = IntArray(1)
        GLES20.glGenTextures(1, textures, 0)
        Utils.checkGLError("glGenTextures")
        textureId = textures[0]
        GLES20.glBindTexture(TEXTURE_TARGET, textureId)
        Utils.checkGLError("glBindTexture $textureId")
        GLES20.glTexParameterf(
            GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
            GLES20.GL_NEAREST.toFloat()
        )
        GLES20.glTexParameterf(
            GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
            GLES20.GL_LINEAR.toFloat()
        )
        GLES20.glTexParameteri(
            GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
            GLES20.GL_CLAMP_TO_EDGE
        )
        GLES20.glTexParameteri(
            GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
            GLES20.GL_CLAMP_TO_EDGE
        )
        Utils.checkGLError("glTexParameter")
        return textureId
    }

    override fun draw() {
        // 绘制前检查有没有错误
        Utils.checkGLError("draw start")
        // 使用当前的程序绘制
        GLES20.glUseProgram(handle)
        Utils.checkGLError("glUseProgram")
        // Set the texture.
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        GLES20.glBindTexture(TEXTURE_TARGET, textureId)

        // 设置着色器参数

        // uMVPMatrix
        GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mVPMatrix, 0)
        Utils.checkGLError("glUniformMatrix4fv")
        // uTexMatrix
        GLES20.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0)
        Utils.checkGLError("glUniformMatrix4fv")
        // aPosition
        GLES20.glEnableVertexAttribArray(maPositionLoc)
        Utils.checkGLError("glEnableVertexAttribArray")
        GLES20.glVertexAttribPointer(
            maPositionLoc, displayCoordsPerVertex,
            GLES20.GL_FLOAT, false, displayVertexStride, displayVertexBuffer
        )
        Utils.checkGLError("glVertexAttribPointer")
        // aTextureCoord
        GLES20.glEnableVertexAttribArray(maTextureCoordLoc)
        Utils.checkGLError("glEnableVertexAttribArray")
        GLES20.glVertexAttribPointer(
            maTextureCoordLoc, textureCoordsPerVertex,
            GLES20.GL_FLOAT, false, textureVertexStride, textureVertexBuffer
        )
        Utils.checkGLError("glVertexAttribPointer")

        // Draw the rect.
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, displayVertexCount)
        Utils.checkGLError("glDrawArrays")
        // Done -- disable vertex array, texture, and program.
        GLES20.glDisableVertexAttribArray(maPositionLoc)
        GLES20.glDisableVertexAttribArray(maTextureCoordLoc)
        GLES20.glBindTexture(TEXTURE_TARGET, 0)
        GLES20.glUseProgram(0)
    }

    //******************************** Shader属性参数 ********************************//

    private var uMVPMatrix: FloatArray? = null

    /**
     * 获取4×4全部为0的矩阵。
     * 不要修改数组里面的数值，这是默认的，不要问为什么。
     */
    private val mVPMatrix: FloatArray?
        get() {
            if (uMVPMatrix == null) {
                uMVPMatrix = FloatArray(16)
                Matrix.setIdentityM(uMVPMatrix, 0)
            }
            return uMVPMatrix
        }

    /**
     * 纹理矩阵，这个矩阵可以用于旋转或者缩放图像，参考[android.graphics.SurfaceTexture.getTransformMatrix]
     */
    var texMatrix: FloatArray? = null

    /*** 转换后的显示矩阵数组 */
    private val convertedDisplayVertexes = FloatArray(8)

    private var mDisplayVertexBuffer: FloatBuffer? = null

    private val displayVertexBuffer: FloatBuffer
        get() {
            if (mDisplayVertexBuffer == null) {
                mDisplayVertexBuffer = Utils.createFloatBuffer(
                    if (!isSizeSets()) {
                        // 还没有设置尺寸信息
                        DISPLAY_VERTEX_MATRIX
                    } else {
                        when (scaleType) {
                            SCALE_FIX_CENTER -> {
                                // 画面居中，不裁剪画面
                                val frameSizeRate: Float = frameWidth / frameHeight.toFloat()
                                val displaySizeRate: Float = displayWidth / displayHeight.toFloat()
                                if (displaySizeRate >= frameSizeRate) {
                                    // 画面左右两边留黑边
                                    val wRate: Float =
                                        (frameWidth * displayHeight).toFloat() / (displayWidth * frameHeight).toFloat()
                                    // bottom left
                                    convertedDisplayVertexes[0] = -wRate
                                    convertedDisplayVertexes[1] = -1f
                                    // bottom right
                                    convertedDisplayVertexes[2] = wRate
                                    convertedDisplayVertexes[3] = -1f
                                    // top left
                                    convertedDisplayVertexes[4] = -wRate
                                    convertedDisplayVertexes[5] = 1f
                                    // top right
                                    convertedDisplayVertexes[6] = wRate
                                    convertedDisplayVertexes[7] = 1f
                                } else {
                                    // 画面上下两边留黑边
                                    val hRate: Float =
                                        (displayWidth * frameHeight).toFloat() / (frameWidth * displayHeight).toFloat()
                                    // bottom left
                                    convertedDisplayVertexes[0] = -1f
                                    convertedDisplayVertexes[1] = -hRate
                                    // bottom right
                                    convertedDisplayVertexes[2] = 1f
                                    convertedDisplayVertexes[3] = -hRate
                                    // top left
                                    convertedDisplayVertexes[4] = -1f
                                    convertedDisplayVertexes[5] = hRate
                                    // top right
                                    convertedDisplayVertexes[6] = 1f
                                    convertedDisplayVertexes[7] = hRate
                                }
                                // return
                                convertedDisplayVertexes
                            }
                            else -> DISPLAY_VERTEX_MATRIX
                        }
                    }
                )
            }
            return mDisplayVertexBuffer!!
        }

    /*** 转换后的纹理矩阵数组 */
    private val convertedTextureVertexes = FloatArray(8)

    private var mTextureVertexBuffer: FloatBuffer? = null

    private val textureVertexBuffer: FloatBuffer
        get() {
            if (mTextureVertexBuffer == null) {
                mTextureVertexBuffer = Utils.createFloatBuffer(
                    if (!isSizeSets()) {
                        // 还没有设置尺寸信息
                        TEXTURE_VERTEX_MATRIX
                    } else {
                        when (scaleType) {
                            SCALE_CENTER_CROP -> {
                                // 画面居中，裁剪画面边缘，使画面铺满显示区域
                                val frameSizeRate: Float = frameWidth / frameHeight.toFloat()
                                val displaySizeRate: Float = displayWidth / displayHeight.toFloat()
                                if (displaySizeRate >= frameSizeRate) {
                                    // 裁剪画面上下两边
                                    val hRate: Float =
                                        (frameWidth * displayHeight).toFloat() / (displayWidth * frameHeight).toFloat()
                                    val bottom: Float = (1f - hRate) / 2f
                                    val top: Float = (1f + hRate) / 2f
                                    // bottom left
                                    convertedTextureVertexes[0] = 0f
                                    convertedTextureVertexes[1] = bottom
                                    // bottom right
                                    convertedTextureVertexes[2] = 1f
                                    convertedTextureVertexes[3] = bottom
                                    // top left
                                    convertedTextureVertexes[4] = 0f
                                    convertedTextureVertexes[5] = top
                                    // top right
                                    convertedTextureVertexes[6] = 1f
                                    convertedTextureVertexes[7] = top
                                } else {
                                    // 裁剪画面左右两边
                                    val wRate: Float =
                                        (displayWidth * frameHeight).toFloat() / (frameWidth * displayHeight).toFloat()
                                    val left: Float = (1f - wRate) / 2f
                                    val right: Float = (1f + wRate) / 2f
                                    // bottom left
                                    convertedTextureVertexes[0] = left
                                    convertedTextureVertexes[1] = 0f
                                    // bottom right
                                    convertedTextureVertexes[2] = right
                                    convertedTextureVertexes[3] = 0f
                                    // top left
                                    convertedTextureVertexes[4] = left
                                    convertedTextureVertexes[5] = 1f
                                    // top right
                                    convertedTextureVertexes[6] = right
                                    convertedTextureVertexes[7] = 1f
                                }
                                // return
                                convertedTextureVertexes
                            }
                            else -> TEXTURE_VERTEX_MATRIX
                        }
                    }
                )
            }
            return mTextureVertexBuffer!!
        }

    private fun resetFloatBuffers() {
        mDisplayVertexBuffer = null
        mTextureVertexBuffer = null
    }

    /*** 画面帧宽度 */
    private var frameWidth: Int = 0

    /*** 画面帧高度 */
    private var frameHeight: Int = 0

    fun setVideoFrameSize(width: Int, height: Int) {
        frameWidth = width
        frameHeight = height
        resetFloatBuffers()
    }

    /*** 显示宽度 */
    private var displayWidth: Int = 0

    /*** 显示高度 */
    private var displayHeight: Int = 0

    fun setDisplaySize(width: Int, height: Int) {
        displayWidth = width
        displayHeight = height
        resetFloatBuffers()
    }

    /*** 判断画面尺寸信息是否设置了 */
    private fun isSizeSets(): Boolean {
        return frameWidth > 0 && frameHeight > 0 && displayWidth > 0 && displayHeight > 0
    }

    /**
     * 拉伸类型[SCALE_FIX_XY]、[SCALE_FIX_CENTER]、[SCALE_CENTER_CROP]
     */
    var scaleType: Int = SCALE_FIX_XY
        set(value) {
            field = value
            resetFloatBuffers()
        }

    companion object {

        const val SCALE_FIX_XY = 1
        const val SCALE_FIX_CENTER = 2
        const val SCALE_CENTER_CROP = 3

        private const val TEXTURE_TARGET = GLES11Ext.GL_TEXTURE_EXTERNAL_OES

        private fun checkLocation(location: Int, label: String) {
            if (location < 0) {
                throw GLException(location, "Unable to locate '$label' in program")
            }
        }

        /**
         * 在显示控件(SurfaceView、TextureView、GLSurfaceView等)中的显示区域矩阵。
         * 以控件的中心为原点，控件边缘到中心的距离为1。
         */
        private val DISPLAY_VERTEX_MATRIX = floatArrayOf(
            -1.0f, -1.0f,   // 0 bottom left
            1.0f, -1.0f,    // 1 bottom right
            -1.0f, 1.0f,    // 2 top left
            1.0f, 1.0f      // 3 top right
        )

        /*** 显示区域矩阵每个顶点坐标的维度，由于图像是2D的，只有x、y两个维度  */
        private const val displayCoordsPerVertex = 2

        /*** 显示区域矩阵顶点个数  */
        private val displayVertexCount = DISPLAY_VERTEX_MATRIX.size / displayCoordsPerVertex

        /*** 显示区域矩阵的每个顶点的占用内存字节大小  */
        private const val displayVertexStride = displayCoordsPerVertex * Utils.SIZEOF_FLOAT

        /**
         * 纹理区域矩阵，可以修改矩阵中的值来达到裁剪图像的目的。
         * 以图像的左下角为原点，图像各边到原点的距离为1。
         */
        private val TEXTURE_VERTEX_MATRIX = floatArrayOf(
            0.0f, 0.0f,     // 0 bottom left
            1.0f, 0.0f,     // 1 bottom right
            0.0f, 1.0f,     // 2 top left
            1.0f, 1.0f      // 3 top right
        )

        /*** 纹理区域矩阵每个顶点坐标的维度，由于图像是2D的，只有x、y两个维度  */
        private const val textureCoordsPerVertex = 2

        /*** 纹理区域矩阵的每个顶点的占用内存字节大小  */
        private const val textureVertexStride = textureCoordsPerVertex * Utils.SIZEOF_FLOAT
    }

}